package jf.collections;

import jf.closure.F1B;
import jf.closure.F1V;
import jf.closure.F2;

import java.util.Collection;
import java.util.Iterator;

/**
 * @author alex
 * @date 17.05.2009
 */
public abstract class AbstractCollectionX<A> implements CollectionX<A>, Collection<A> {
	/**
	 * Return base collection for this class
	 *
	 * @return
	 */
	protected abstract Collection<A> getBaseCollection();

	/**
	 * Implement iterator from right to left
	 *
	 * @return
	 */
	public abstract Iterator<A> rightIterator();

	public A last() {
		Iterator<A> itr = this.rightIterator();
		if (itr.hasNext()) {
			return itr.next();
		} else {
			return null;
		}
	}

	public A head() {
		Iterator<A> itr = this.iterator();
		if (itr.hasNext()) {
			return itr.next();
		} else {
			return null;
		}
	}

	public Option<A> find(F1B<A> f) {
		Option<A> option = Option.none();
		for (A a : this) {
			if (f.apply(a)) return Option.some(a);
		}
		return option;
	}

	public <B> B foldLeft(B b, F2<B, A, B> f) {
		for (A a : this) {
			b = f.apply(b, a);
		}
		return b;
	}

	public <B> B foldRight(B b, F2<B, A, B> f) {
		Iterator<A> itr = rightIterator();
		while (itr.hasNext()) {
			A a = itr.next();
			b = f.apply(b, a);
		}
		return b;
	}

	public boolean forall(F1B<A> f) {
		for (A a : this) {
			if (!f.apply(a)) return false;
		}
		return true;
	}

	public void foreach(F1V<A> f) {
		for (A a : this) {
			f.apply(a);
		}
	}

	@Override
	public int hashCode() {
		return getBaseCollection().hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		return getBaseCollection().equals(obj);
	}

	public String toString(String delimeter, String head, String tail) {
		StringBuilder result = new StringBuilder();
		result.append(head);
		int max = size() - 1;
		int i = 0;
		for (A a : this) {
			result.append(a);
			if (i < max) result.append(delimeter);
			i++;
		}
		result.append(tail);
		return result.toString();
	}

	@Override
	public String toString() {
		return toString(", ", getClass().getSimpleName() + "[", "]");
	}

	public Iterator<A> leftIterator() {
		return iterator();
	}


	public int size() {
		return getBaseCollection().size();
	}

	public boolean isEmpty() {
		return getBaseCollection().isEmpty();
	}

	public boolean contains(Object o) {
		return getBaseCollection().contains(o);
	}

	public Iterator<A> iterator() {
		return getBaseCollection().iterator();
	}

	public Object[] toArray() {
		return getBaseCollection().toArray();
	}

	public <T> T[] toArray(T[] a) {
		return getBaseCollection().toArray(a);
	}

	public boolean add(A a) {
		return getBaseCollection().add(a);
	}

	public boolean remove(Object o) {
		return getBaseCollection().remove(o);
	}

	public boolean containsAll(Collection<?> c) {
		return getBaseCollection().containsAll(c);
	}

	public boolean addAll(Collection<? extends A> c) {
		return getBaseCollection().addAll(c);
	}

	public boolean removeAll(Collection<?> c) {
		return getBaseCollection().removeAll(c);
	}

	public boolean retainAll(Collection<?> c) {
		return getBaseCollection().retainAll(c);
	}

	public void clear() {
		getBaseCollection().clear();
	}
}
